package CoroutineTool

import (
	"sync"
)

//生命周期 DefineGo->AddeGo->DoneGo->WaitGo 相当于 sync.WaitGroup->sync.WaitGroup.add(1)->sync.WaitGroup.Done->sync.WaitGroup.Wait()

// 代表每一个节点
type node struct {
	data interface{}
	next *node
}

type queue struct {
	// 头节点
	head *node

	// 队尾节点
	rear *node

	size int
	//互斥锁
	sync.Mutex
}

func NewQueue() *queue {
	q := new(queue)
	q.head = nil
	q.rear = nil
	q.size = 0
	return q
}

// Put 尾插法
func (q *queue) Put(element interface{}) {
	q.Lock()
	defer q.Unlock()
	n := new(node)
	n.data = element

	if q.rear == nil {
		q.head = n
		q.rear = n
	} else {
		q.rear.next = n
		q.rear = n
	}
	q.size++
}

// PutHead 头插法，在队列头部插入一个元素
func (q *queue) PutHead(element interface{}) {
	q.Lock()
	defer q.Unlock()
	n := new(node)
	n.data = element
	if q.head == nil {
		q.head = n
		q.rear = n
	} else {
		n.next = q.head
		q.head = n
	}
	q.size++
}

// Get 获取并删除队列头部的元素
func (q *queue) Get() interface{} {
	q.Lock()
	defer q.Unlock()
	if q.head == nil {
		return nil
	}
	n := q.head
	// 代表队列中仅一个元素
	if n.next == nil {
		q.head = nil
		q.rear = nil

	} else {
		q.head = n.next
	}
	q.size--
	return n.data
}

// 获取队列长度
func (q *queue) Size() int {
	q.Lock()
	defer q.Unlock()
	NowSize := q.size
	return NowSize
}

type waitGroup struct {
	sync.WaitGroup
}

type WaitGroupQueue struct {
	wg      *waitGroup
	q       *queue
	startGo int
}

// 定义变量
func DefineGo(startGo int) *WaitGroupQueue {
	wq := new(WaitGroupQueue)
	wg := new(waitGroup)
	q := NewQueue()
	wq.wg = wg
	wq.q = q
	if startGo <= 0 {
		wq.startGo = 1
	} else {
		wq.startGo = startGo
	}
	return wq
}

// 在go func上面使用
func AddGo(wq *WaitGroupQueue) *WaitGroupQueue {
	for {
		if wq.q.Size() < wq.startGo {
			break
		}
	}
	wq.q.Put(1)
	wq.wg.Add(1)
	return wq
}

// 在go func最后使用
func DoneGo(wq *WaitGroupQueue) {
	wq.q.Get()
	wq.wg.Done()
}

// 等待go func完成
func WaitGo(wq *WaitGroupQueue) {
	wq.wg.Wait()
}

/*协程池例子：
func DefineGoTest() {
	// 定义一个协程池
	wq := CoroutineTool.DefineGo(2000)
	for i := 0; i < 100000; i++ {
		// 添加协程
		CoroutineTool.AddGo(wq)
		go func() {
			// 通知协程池已经完成
			defer CoroutineTool.DoneGo(wq)
			// do something
		}()
	}
	// 等待协程池完成
	CoroutineTool.WaitGo(wq)
}
*/
